#include <iostream>
#include <string>
#include <mutex>
#include <thread>
#include <future>

#include "rtc_base/ssl_adapter.h"
#include "rtc_base/thread.h"
#include "rtc_base/logging.h"
#include "rtc_base/strings/json.h"

#include "api/peer_connection_interface.h"
#include "modules/audio_device/include/audio_device.h"
#include "p2p/base/stun_server.h"

#include "openssl/hmac.h"
#include "rtc_base/third_party/base64/base64.h"

#include "PeerConnectionManager.h"

#include "examples/webrtc-client/easywsclient.hpp"

bool stopedWebrtc = false;
std::string host = "test.iimiim.cn";
std::string userNameInternalWebrtc = "test001";

static easywsclient::WebSocket::pointer websocket_client_webrtc = NULL;
std::mutex websocket_lock;
bool loginInSuccessWebrtc = false;
PeerConnectionManager *webRtcServerPtr = NULL;

void websocketSendMessage(const std::string& message) {
    std::lock_guard<std::mutex> lock_ws(websocket_lock, std::adopt_lock);
    if(websocket_client_webrtc == NULL) {
        RTC_LOG(INFO) << "WebSocket closed, can not send message";
        return;
    }
    easywsclient::WebSocket::readyStateValues webSocketState;
    webSocketState = websocket_client_webrtc->getReadyState();
    if(webSocketState == easywsclient::WebSocket::OPEN) {
        websocket_client_webrtc->send(message.c_str());
    } else {
        RTC_LOG(INFO) << "WebSocket not opened, can not send message";
    }
}

void handleLogin(Json::Value jmessage) {
    bool login_success = false;
    rtc::GetBoolFromJson(jmessage["success"], &login_success);
    if(!loginInSuccessWebrtc) {
        if(login_success){
            loginInSuccessWebrtc = true;
        } else {
            fprintf(stderr, "handleLogin: device name invaild\n");
            srand((unsigned)time(NULL));
            userNameInternalWebrtc = userNameInternalWebrtc + "-" + std::to_string(rand());
            fprintf(stdout, "handleLogin: create new device name %s\n", userNameInternalWebrtc.c_str());
            Json::Value payload_type("login");
            Json::Value payload_name(userNameInternalWebrtc);
            Json::Value payload;
            payload["type"] = payload_type;
            payload["name"] = payload_name;
            std::string payload_str = rtc::JsonValueToString(payload);
            websocketSendMessage(payload_str);
        }
    } else {
        fprintf(stdout, "handleLogin: loginInSuccessWebrtc already\n");
    }
}

void handle_message(const std::string& message) {
    fprintf(stdout, "got message%s\n", message.c_str());
    Json::CharReaderBuilder reader;
    std::string json_errs;
    Json::Value jmessage;
    std::istringstream message_stream(message);
    if (!Json::parseFromStream(reader, message_stream, &jmessage, &json_errs)) {
        return;
    }
    std::string message_type;
    rtc::GetStringFromJson(jmessage["type"], &message_type);
    std::string peerid;
    rtc::GetStringFromJson(jmessage["from"], &peerid);
    
    if(message_type == "message") {
        std::string message_payload = rtc::JsonValueToString(jmessage["payload"]);
        std::string message_payload_str = "";
        for(std::string::const_iterator it = message_payload.begin(); it != message_payload.end(); ++it) {
            if (*it != '"') {
                message_payload_str += *it;
            }
        }
    } else if(message_type == "save-data-over") {
        //conductor->saveDataOver = true;
        //conductor->setDataChannelStatus(false);
    } else if(message_type == "login") {
        handleLogin(jmessage);
    } else if(message_type == "file-transform-parameter-accept") {
    } else if(message_type == "file-transform-cancel") {
    } else if(message_type == "file-transform") {
    } else if(message_type == "offer-loopback") {
        std::string videourl = "H264 USB Camera";
        std::string audiourl = "";
        std::string options = "width=640&height=480";
        Json::Value sdp_json = webRtcServerPtr->createOffer(peerid, videourl, audiourl, options);
        Json::Value offer_json;
        offer_json["payload"] = sdp_json;
        Json::Value peer_name(peerid);
        offer_json["name"] = peer_name;
        Json::Value type_local("offer");
        offer_json["type"] = type_local;
        std::string payload_str = rtc::JsonValueToString(offer_json);
        websocketSendMessage(payload_str);
    } else if(message_type == "offer") {
        //conductor->handleOffer(jmessage["payload"]);
    } else if(message_type == "answer") {
        webRtcServerPtr->setAnswer(peerid, jmessage["payload"]);
    } else if(message_type == "candidate") {
        webRtcServerPtr->addIceCandidate(peerid, jmessage["payload"]);
        Json::Value iceCandidateList = webRtcServerPtr->getIceCandidateList(peerid);
        if(iceCandidateList.size() > 0){
            std::string candidate_str = rtc::JsonValueToString(iceCandidateList);
            if(webRtcServerPtr->sendCadateString != candidate_str){
                webRtcServerPtr->sendCadateString = candidate_str;
                Json::Value candidate_json;
                candidate_json["payload"] = iceCandidateList;
                Json::Value peer_name(peerid);
                candidate_json["name"] = peer_name;
                Json::Value type_local("candidate_list");
                candidate_json["type"] = type_local;
                std::string payload_str = rtc::JsonValueToString(candidate_json);
                websocketSendMessage(payload_str);
            }
        }
    } else if(message_type == "leave") {
        webRtcServerPtr->hangUp(peerid);
    } else if(message_type == "server_kick_out") {
    } else if(message_type == "pong-signal") {
        ;
    } else {
        ;
    }
}

void* websocketThread(void* args) {
    PeerConnectionManager *webRtcServer = (PeerConnectionManager *)args;
    while(!stopedWebrtc) {
        std::this_thread::sleep_for(std::chrono::microseconds(3 * 1000000));
        Json::Value payload = webRtcServer->getMediaList();
        RTC_LOG(INFO) << "getMediaList " <<rtc::JsonValueToString(payload);

        payload = webRtcServer->getVideoDeviceList();
        RTC_LOG(INFO) << "getVideoDeviceList " <<rtc::JsonValueToString(payload);

        payload = webRtcServer->getAudioDeviceList();
        RTC_LOG(INFO) << "getAudioDeviceList " <<rtc::JsonValueToString(payload);

        payload = webRtcServer->getIceServers("");
        RTC_LOG(INFO) << "getIceServers " <<rtc::JsonValueToString(payload);

        payload = webRtcServer->getStreamList();
        RTC_LOG(INFO) << "getStreamList " <<rtc::JsonValueToString(payload);

        payload = webRtcServer->getPeerConnectionList();
        RTC_LOG(INFO) << "getPeerConnectionList " <<rtc::JsonValueToString(payload);

        payload = webRtcServer->getIceCandidateList("");
        RTC_LOG(INFO) << "getIceCandidateList " <<rtc::JsonValueToString(payload);
        break;
    }

    easywsclient::WebSocket::readyStateValues webSocketState;
    {
        std::lock_guard<std::mutex> lock_ws(websocket_lock, std::adopt_lock);
        std::string port = "3000";
        websocket_client_webrtc = easywsclient::WebSocket::from_url("ws://" + host + ":" + port);
        if(websocket_client_webrtc) {
            RTC_LOG(INFO) << "WebSocket created success";
        } else {
            RTC_LOG(LERROR) << "WebSocket create failed";
            return NULL;
        }
        webSocketState = websocket_client_webrtc->getReadyState();
    }
    if(!loginInSuccessWebrtc) {
        Json::Value payload_type("login");
        Json::Value payload_name(userNameInternalWebrtc);
        Json::Value payload;
        payload["type"] = payload_type;
        payload["name"] = payload_name;
        std::string payload_str = rtc::JsonValueToString(payload);
        websocketSendMessage(payload_str);
    }
    while (webSocketState == easywsclient::WebSocket::OPEN) {
        if(stopedWebrtc) break;
        {
            std::lock_guard<std::mutex> lock_ws(websocket_lock, std::adopt_lock);
            websocket_client_webrtc->poll();
            websocket_client_webrtc->dispatch(handle_message);
        }

        std::this_thread::sleep_for(std::chrono::microseconds(100 * 1000));
        {
            std::lock_guard<std::mutex> lock_ws(websocket_lock, std::adopt_lock);
            webSocketState = websocket_client_webrtc->getReadyState();
            if(webSocketState != easywsclient::WebSocket::OPEN) {
                fprintf(stderr, "webSocketState != easywsclient::WebSocket::OPEN\n");
                exit(-1);
            } else {
                ; //fprintf(stdout, "web_SocketClient: webSocketState: OPEN SUCCESS\n");
            }
        }
    }
    delete websocket_client_webrtc;
    websocket_client_webrtc = NULL;
    pthread_exit(NULL);
}

int main(int argc, char **argv){
    int logLevel = rtc::INFO;
    
    rtc::LogMessage::LogToDebug((rtc::LoggingSeverity)logLevel);
    rtc::LogMessage::LogTimestamps();
    rtc::LogMessage::LogThreads();
    RTC_LOG(INFO) << "Logger level:" <<  rtc::LogMessage::GetLogToDebug();

    rtc::Thread* thread = rtc::Thread::Current();
    rtc::InitializeSSL();

    /*
    std::string coturnUserName = GetServerTimestamp();
    coturnUserName = coturnUserName + ":" + userNameInternalWebrtc;
    unsigned char digest[EVP_MAX_MD_SIZE] = {'\0'};
    unsigned int digest_len = 0;
    char turn_key[] = "t=u408021891333";
    HMAC(EVP_sha1(), turn_key, strlen(turn_key), (unsigned char*)coturnUserName.c_str(),
         coturnUserName.size(), digest, &digest_len);
    std::string password;
    rtc::Base64::EncodeFromArray(digest, digest_len, &password);
    */

    std::list<std::string> iceServerList;
    std::string coturnUserName = "webrtciimuserzxzq";
    std::string password = "zxzq852258";
    iceServerList.push_back("turn:" + coturnUserName + "|" + password + "@" + host + ":3478?transport=udp");
    iceServerList.push_back("turn:" + coturnUserName + "|" + password + "@" + host + ":3478?transport=tcp");
    iceServerList.push_back("turn:" + coturnUserName + "|" + password + "@" + host + ":5349?transport=udp");
    iceServerList.push_back("turn:" + coturnUserName + "|" + password + "@" + host + ":5349?transport=tcp");
    std::map<std::string,std::string> urlVideoList;
    std::map<std::string,std::string> urlAudioList;
    webrtc::AudioDeviceModule::AudioLayer audioLayer = webrtc::AudioDeviceModule::kPlatformDefaultAudio;
    std::string publishFilter(".*");

    PeerConnectionManager webRtcServer(iceServerList, urlVideoList, urlAudioList, audioLayer, publishFilter);
    if (!webRtcServer.InitializePeerConnection()) {
        RTC_LOG(INFO) << "Cannot Initialize WebRTC server";
    } else {
        webRtcServerPtr = &webRtcServer;
        pthread_t pthread_id;
        pthread_create(&pthread_id, NULL, websocketThread, &webRtcServer);
        thread->Run();
        RTC_LOG(INFO) << "mainloop exit";
        stopedWebrtc = true;
        pthread_join(pthread_id, NULL);
        RTC_LOG(INFO) << "stopedWebrtc exit";
    }
    rtc::CleanupSSL();
    return 0;
}
